#include "my_http_utils.h"
#include "my_http_base.h"

my_result_t
my_http_readline(my_http_readline_sm_t *readline_sm, my_buf_t *buf)
{
    char ch, *p;

    enum {
        SW_START = 0,
        SW_PROGRESS,
        SW_END
    } state;

    if (readline_sm->state == SW_END) {
        my_http_readline_sm_reset(readline_sm);
    }

    state = readline_sm->state;
    for (p = buf->pos; p != buf->end; p++) {
        ch = *p;

        switch(state) {
        case SW_START:
            if (ch == '\0') {
                return MY_HTTP_READLINE_FAILED;
            }

            state = SW_PROGRESS;
            readline_sm->line.data = p;

            break;

        case SW_PROGRESS:
            if (ch == '\0') {
                return MY_HTTP_READLINE_FAILED;
            }

            if (ch == LF) {
                buf->pos = p + 1;
                readline_sm->line.len = p + 1 - readline_sm->line.data;
                readline_sm->state = SW_END;
                return MY_SUCCESS;
            }

            break;

        default:
            MY_LOG_FATAL("unreachable");
        }

        if (p + 1 - readline_sm->line.data > MY_HTTP_MAX_LINE_LENGTH) {
            return MY_HTTP_READLINE_FAILED;
        }
    }

    buf->pos = p;
    readline_sm->state = state;
    return MY_AGAIN;
}

my_bool_t
my_http_empty_line(my_http_readline_sm_t *readline_sm)
{
    char *p = readline_sm->line.data;
    char *end = p + readline_sm->line.len;

    /* skip space char */
    while (p != end) {
        if (*p != ' ') {
            break;
        }

        p++;
    }

    if (*p == CR) {
        p++;
    }

    return *p == LF ? MY_TRUE : MY_FALSE;
}

my_result_t
my_http_chunk_size_parse(my_http_chunk_sm_t *sm, my_str_t *line)
{
    char *p     = line->data;
    char *end   = p + line->len;
    char c, ch;

    if (p == end || *(end - 1) != LF) {
        return MY_HTTP_INVALID_CHUNK_LINE;
    }

    if (sm->state == SW_CHUNK_START) {
        c = *p;
        ch = c | 0x20;

        if (c >= '0' && c <= '9') {
            sm->size = c - '0';
        } else if (ch >= 'a' && ch <= 'f') {
            sm->size = ch - 'a' + 10;
        } else {
            return MY_HTTP_INVALID_CHUNK_LINE;
        }

        sm->state = SW_CHUNK_SIZE;
        p++;
    }

    if (sm->state != SW_CHUNK_SIZE) {
        MY_LOG_FATAL("unexcept state");
    }

    for (; p != end; p++) {
        c = *p;
        ch = c | 0x20;

        if (c >= '0' && c <= '9') {
            sm->size = sm->size * 16 + (c - '0');
        } else if (ch >= 'a' && ch <= 'f') {
            sm->size = sm->size * 16 + (ch - 'a' + 10);
        } else {
            /* chunk_extension, ignore */
            sm->remain = sm->size;
            sm->state = SW_CHUNK_DATA;
            return MY_SUCCESS;
        }

        p++;
    }

    return MY_AGAIN;
}